import * as Cesium from "cesium";
import * as dat from "dat.gui";

export default class HighFogEffect {
  constructor(viewer, options) {
    if (!viewer) throw new Error("no viewer object!");
    options = options || {};

    this.viewer = viewer;
    this.visibility = Cesium.defaultValue(options.visibility, 0.5);
    this.color = Cesium.defaultValue(
      options.color,
      new Cesium.Color(0.8, 0.82, 0.84)
    );
    this.minHeight = Cesium.defaultValue(options.minHeight, 0.0);
    this.maxHeight = Cesium.defaultValue(options.maxHeight, 600.0);
    this._show = Cesium.defaultValue(options.show, true);

    this.init();
  }

  init() {
    console.log(this.viewer.camera);
    // magnitude计算笛卡尔的量级（长度）
    console.log(Cesium.Cartesian3.magnitude(this.viewer.camera.positionWC));
    this.viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(114.1, 25.1, 4000),
      orientation: {
        pitch: -0.6,
      },
    });
    this.fogStage = new Cesium.PostProcessStage({
      name: "czm_realHeightFog",
      fragmentShader: this.fog(),
      uniforms: {
        visibility: () => this.visibility,
        fogColor: () => this.color,
        minHeight: () => this.minHeight,
        maxHeight: () => this.maxHeight,
        u_globalDensity: () => 0.6,
        u_fogHeight: () => this.maxHeight,
        u_earthRadiusOnCamera: () =>
          Cesium.Cartesian3.magnitude(this.viewer.camera.positionWC) -
          this.viewer.camera.positionCartographic.height,
        u_cameraHeight: () => this.viewer.camera.positionCartographic.height
      },
    });

    this.viewer.scene.postProcessStages.add(this.fogStage);
    // this.addGui(this.fogStage.uniforms);
  }

  addGui(uniformMap) {
    const gui = new dat.GUI();
    gui.add(this, "visibility", 0.0, 1.0).step(0.1).name("雾浓度");
    // gui.addColor(this, "color").name("雾颜色");
    gui.add(this, "minHeight", 0.0, 1000.0).step(50).name("起始雾高");
    gui.add(this, "maxHeight", 100.0, 8500.0).step(10).name("最大雾高");
    gui
      .add(this, "_show")
      .name("显示雾效")
      .onChange((v) => this.show(v));
  }

  show(visible) {
    this._show = visible;
    if (this.fogStage) this.fogStage.enabled = visible;
  }

  destroy() {
    if (this.viewer && this.fogStage) {
      this.viewer.scene.postProcessStages.remove(this.fogStage);
    }
    if (this._terrainInterval) clearInterval(this._terrainInterval);
  }

  fog() {
    return `
    precision mediump float;
    uniform sampler2D colorTexture;
    uniform sampler2D depthTexture;
    uniform float visibility;
    uniform vec3 fogColor;
    uniform float u_earthRadiusOnCamera;
    uniform float u_cameraHeight;
    uniform float u_fogHeight;
    uniform float u_globalDensity;

    in vec2 v_textureCoordinates;
    out vec4 myColor;

    vec4 getWorldCoordinate(sampler2D depthTexture, vec2 texCoords) {
        float depth = czm_unpackDepth(texture(depthTexture, texCoords));
        vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
        eyeCoordinate = eyeCoordinate / eyeCoordinate.w;
        vec4 worldCoordinate = czm_inverseView * eyeCoordinate;
        worldCoordinate = worldCoordinate / worldCoordinate.w;
        return worldCoordinate;
    }

    float getRoughHeight(vec4 worldCoordinate, float earthRadiusOnCamera) {
        float disToCenter = length(vec3(worldCoordinate));
        return disToCenter - earthRadiusOnCamera;
    }

    // 简单的线性高度雾
    float sampleLinearHeightFog(float fogHeight, float pixelHeight, float maxAlpha) {
    	float fog = clamp(1.0 - pixelHeight / fogHeight, 0.0, maxAlpha);
    	return fog;
    }

    // 得到a向量在b向量的投影长度，如果同向结果为正，异向结果为负
    float projectVector(vec3 a, vec3 b) {
    	float scale = dot(a, b) / dot(b, b);
    	float k = scale / abs(scale);
    	return k * length(scale * b);
    }
    // 浓度积分高度雾
    float linearHeightFog(vec3 positionToCamera, float cameraHeight, float pixelHeight, float fogMaxHeight) {
    	float globalDensity = u_globalDensity / 10.0;
    	vec3 up = -1.0 * normalize(czm_viewerPositionWC);
    	float vh = projectVector(normalize(positionToCamera), up);
    	float b = mix(cameraHeight, fogMaxHeight, step(fogMaxHeight, cameraHeight));
    	float a = mix(pixelHeight, fogMaxHeight, step(fogMaxHeight, pixelHeight));
    	float fog = (b - a) - 0.5 * (pow(b, 2.0) - pow(a, 2.0)) / fogMaxHeight;
    	fog = globalDensity * fog / vh;
    	fog = mix(0.0, 1.0, fog / (fog + 1.0));
    	return fog;
    }

    void main(void) {
        vec4 origncolor = texture(colorTexture, v_textureCoordinates);
        vec4 positionWC = getWorldCoordinate(depthTexture, v_textureCoordinates);
        float pixelHeight = getRoughHeight(positionWC, u_earthRadiusOnCamera);
        vec3 positionToCamera = vec3(vec3(positionWC) - czm_viewerPositionWC);
	      float fogFactor = linearHeightFog(positionToCamera, u_cameraHeight, pixelHeight, u_fogHeight);
        // float fogFactor = sampleLinearHeightFog(u_fogHeight, pixelHeight, 1.0);
        fogFactor *= visibility;

        myColor = mix(origncolor, vec4(fogColor, 1.0), fogFactor);
    }
    `;
  }
}
